using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

using FluentBinding.WinForms.Controls;

namespace FluentBinding.WinForms
{
    /// <summary>
    /// TODO
    /// *   create demo
    /// *   add DataSourceUpdateMode support
    /// *   add ControlUpdateMode
    /// *   add ListBox tests
    /// *   consider adding support for nullable types
    /// *   add support for null notifiers
    /// *   add support for notifiers which can be changed in runtime
    /// *   add support to one way binding from control to entity
    /// *   implement runtime data sources for combo box
    /// *   add more properties for Control
    /// *   investigate how it works with validation \ error providers
    /// </summary>
    /// <typeparam name="E">Type of the root entity</typeparam>
    public class Bindings<E>
        where E : class
    {
        #region Fields

        private readonly List<BindingProxy<E>> _bindingUnitsWithPaths = new List<BindingProxy<E>>();

        #endregion

        #region Constructors

        public Bindings(E entity)
        {
            Entity = entity;
        }

        #endregion

        #region Public properties

        public E Entity { get; private set; }

        #endregion

        #region Public methods

        public IControlBinding<E> Bind(params Control[] controls)
        {
            EnsureHandleIsNotCreated(controls);

            return new ControlBinding<E>(CreateBindingProxy(controls));
        }

        public ICheckBoxBinding<E> Bind(params CheckBox[] checkBoxes)
        {
            EnsureHandleIsNotCreated(checkBoxes);

            return new CheckBoxBinding<E>(CreateBindingProxy(checkBoxes));
        }

        public IComboBoxBinding<E> Bind(params ComboBox[] controls)
        {
            EnsureHandleIsNotCreated(controls);

            return new ComboBoxBinding<E>(CreateBindingProxy(controls));
        }

        public IDateTimePickerBinding<E> Bind(params DateTimePicker[] dateTimePickers)
        {
            EnsureHandleIsNotCreated(dateTimePickers);

            return new DateTimePickerBinding<E>(CreateBindingProxy(dateTimePickers));
        }

        #endregion

        #region Private or internal methods

        private void EnsureHandleIsNotCreated(Control[] controls)
        {
            controls.ForEach(x =>
            {
                if (x.IsHandleCreated)
                    throw new InvalidOperationException("Binding setup should be done before form is shown");                                     
            });
        }

        private BindingProxy<E> CreateBindingProxy(Control[] controls)
        {
            var bindingUnit = new BindingProxy<E>(Entity, controls);

            bindingUnit.DataMemberUpdated +=
                (sender, e) => ((BindingProxy<E>)sender).AffectedEntities.ForEach(NotifyEffectedBindings);

            bindingUnit.EntityPathUpdated +=
                (sender, e) => _bindingUnitsWithPaths.Add(bindingUnit);

            return bindingUnit;
        }

        private void NotifyEffectedBindings(string entityPath)
        {
            _bindingUnitsWithPaths.Where(x => x.EntityPath.Contains(entityPath))
                .ForEach(x => x.NotifyEntityIsUpdated());
        }

        #endregion
    }
}